#include "publishdata.h"
#include "converter.h"


PublishHelperPtr CreatePublishHelper(MsgExpress::PublishData* pub,bool load)
{
	if(pub==NULL)
		return PublishHelperPtr();
	IPublishHelper* helper=new PublishHelper(pub,load);
	return (PublishHelperPtr)helper;
}
PublishHelperPtr CreatePublishHelper(MsgExpress::SubscribeData* sub,int topic,int subId)
{
	if(sub==NULL)
		return PublishHelperPtr();
	sub->set_topic(topic);
	sub->set_subid(subId);
	IPublishHelper* helper=new PublishHelper(sub);
	return (PublishHelperPtr)helper;
}

DataItem::DataItem(int key,MsgExpress::DataType type)
{
	mKey=key;
	mType=type;
}
DataItem::~DataItem()
{
	vecValues.clear();
}
int DataItem::GetKey()const
{
	return mKey;
}
MsgExpress::DataType DataItem::GetType()const
{
	return mType;
}
int DataItem::GetDataNumber()const
{
	return vecValues.size();
}
std::string DataItem::GetString(int index)const
{
	if(GetType()!=MsgExpress::STRING)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return vecValues[index];
}

std::string DataItem::GetRaw(int index)const
{
	if(GetType()!=MsgExpress::BINARY)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return vecValues[index];
}

int DataItem::GetInt32(int index)const
{
	if(GetType()!=MsgExpress::INT32)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return ntohl(*(int*)vecValues[index].data());
}
unsigned int DataItem::GetUInt32(int index)const
{
	if(GetType()!=MsgExpress::UINT32)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return ntohl(*(unsigned int*)vecValues[index].data());
}
int64_t DataItem::GetInt64(int index)const
{
	if(GetType()!=MsgExpress::INT64)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return ntohll(*(int64_t*)vecValues[index].data());
}
uint64_t DataItem::GetUInt64(int index)const
{
	if(GetType()!=MsgExpress::UINT64)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return ntohll(*(uint64_t*)vecValues[index].data());
}
float DataItem::GetFloat(int index)const
{
	if(GetType()!=MsgExpress::FLOAT)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return *(float*)vecValues[index].data();
}
double DataItem::GetDouble(int index)const
{
	if(GetType()!=MsgExpress::DOUBLE)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return *(double*)vecValues[index].data();
}
time_t DataItem::GetDatetime(int index)const
{
	if(GetType()!=MsgExpress::DATETIME)
		throw "Data type mismatch";
	if(index<0 || index>=GetDataNumber())
		throw "Index out of bound";
	return ntohll(*(time_t*)vecValues[index].data());
}

void DataItem::AddValue(std::string value)
{
	vecValues.push_back(value);
}
const std::vector<std::string>& DataItem::GetValues()const
{
	return vecValues;
}

PublishData::PublishData()
{
	mapKeyValues=new std::unordered_map<int,DataItem*>();
}

PublishData::PublishData(int topic)
{
	mTopic=topic;
	mapKeyValues=new std::unordered_map<int,DataItem*>();
}

PublishData::~PublishData()
{
	std::unordered_map<int,DataItem*>::iterator it=mapKeyValues->begin();
	while(it!=mapKeyValues->end())
	{
		delete it->second;
		it++;
	}
	mapKeyValues->clear();
	delete mapKeyValues;
}
void PublishData::SetPubData(const MsgExpress::PublishData& pub)
{
	SetTopic(pub.topic());
	mIds.clear();
	for(int i=0;i<pub.subid_size();i++)
		mIds.push_back(pub.subid(i));
	mapKeyValues->clear();
	for(int i=0;i<pub.item_size();i++)
	{
		//for(int j=0;j<pub.item(i).value_size();j++)
		//	AddValue(pub.item(i).key(),pub.item(i).type(),pub.item(i).value(j));
	}
}
void PublishData::GetPubData(MsgExpress::PublishData& pub)const
{
	pub.set_topic(mTopic);
	std::unordered_map<int,DataItem*>::const_iterator it=mapKeyValues->begin();
	while(it!=mapKeyValues->end())
	{
		MsgExpress::DataItem* item=pub.add_item();
		item->set_key(it->second->GetKey());
		//item->set_type(it->second->GetType());
		const std::vector<std::string>& values=it->second->GetValues();
		std::vector<std::string>::const_iterator iter=values.begin();
		while(iter!=values.end())
		{
			//item->add_value(*iter);
			iter++;
		}
		it++;
	}
}

void PublishData::SetTopic(int topic)
{
	mTopic=topic;
}
int PublishData::GetTopic()const
{
	return mTopic;
}
void PublishData::GetSubIds(std::vector<int>& Ids)const
{
	Ids=mIds;
}
bool PublishData::AddValue(int key,MsgExpress::DataType type,std::string value)
{
	DataItem* item=(*mapKeyValues)[key];
	if(!item)
	{
		item=new DataItem(key,type);
		(*mapKeyValues)[key]=item;
	}
	else
	{
		if(item->GetType()!=type)
			return false;
	}
	item->AddValue(value);
	return true;
}
bool PublishData::AddString(int key,std::string value)
{
	return AddValue(key,MsgExpress::STRING,value);
}
bool PublishData::AddRaw(int key,const char * str,size_t size)
{
	return AddValue(key,MsgExpress::BINARY,string(str,size));
}
#define ADDVALUE(key,type,value) \
	std::string str; \
	str.resize(sizeof(value)); \
	memcpy((void*)(str.data()),&value,str.size()); \
	return AddValue(key,type,str); 

bool PublishData::AddInt32(int key,int value)
{
	value=htonl(value);
	ADDVALUE(key,MsgExpress::INT32,value);
}
bool PublishData::AddUInt32(int key,unsigned int value)
{
	value=htonl(value);
	ADDVALUE(key,MsgExpress::UINT32,value);
}
bool PublishData::AddInt64(int key,int64_t value)
{
	value=htonll(value);
	ADDVALUE(key,MsgExpress::INT64,value)
}
bool PublishData::AddUInt64(int key,uint64_t value)
{
	value=htonll(value);
	ADDVALUE(key,MsgExpress::UINT64,value)
}
bool PublishData::AddFloat(int key,float value)
{
	ADDVALUE(key,MsgExpress::FLOAT,value)
}
bool PublishData::AddDouble(int key,double value)
{
	ADDVALUE(key,MsgExpress::DOUBLE,value)
}
bool PublishData::AddDatetime(int key,time_t value)
{
	value=htonll(value);
	ADDVALUE(key,MsgExpress::DATETIME,value)
}
const void PublishData::GetKeys(std::vector<int>& Keys)const
{
	Keys.clear();
	std::unordered_map<int,DataItem*>::const_iterator it=mapKeyValues->begin();
	while(it!=mapKeyValues->end())
	{
		Keys.push_back(it->first);
		it++;
	}
}
const IDataItem* PublishData::GetDataItem(int key)const
{
	std::unordered_map<int,DataItem*>::const_iterator it=mapKeyValues->find(key);
	if(it!=mapKeyValues->end())
		return it->second;
	return NULL;
}

MsgExpress::DataItem* PublishHelper::CreateItem(int key,MsgExpress::DataType type)
{
	MsgExpress::DataItem* item=0;
	if(mapItems.find(key)!=mapItems.end())
	{
		item=mapItems[key];
		//if(item->type()!=type)
		//	return NULL;
	}
	else{
		if(pub)
		    item=pub->add_item();
		else
			item=sub->add_condition();
		item->set_key(key);
		//item->set_type(type);
		mapItems[key]=item;
	}
	return item;
}
#define ADD_VALUE(item,value) \
	std::string str; \
	str.resize(sizeof(value)); \
	memcpy((void*)(str.data()),&value,str.size()); \
	item->add_value(str);


bool PublishHelper::AddString(int key,const std::string& value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::STRING);
	if(item==NULL)
		return false;
	item->add_strval(value);
	return true;
}
bool PublishHelper::AddBinary(int key,const void * value,size_t size)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::BINARY);
	if(item==NULL)
		return false;
	item->add_rawval(value,size);
	return true;
}
bool PublishHelper::AddInt32(int key,int value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::INT32);
	if(item==NULL)
		return false;
	item->add_ival(value);
	value=htonl(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddUInt32(int key,unsigned int value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::UINT32);
	if(item==NULL)
		return false;
	item->add_uival(value);
	value=htonl(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddInt64(int key,int64_t value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::INT64);
	if(item==NULL)
		return false;
	item->add_lval(value);
	value=htonll(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddUInt64(int key,uint64_t value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::UINT64);
	if(item==NULL)
		return false;
	item->add_ulval(value);
	value=htonll(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddFloat(int key,float value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::FLOAT);
	if(item==NULL)
		return false;
	item->add_fval(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddDouble(int key,double value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::DOUBLE);
	if(item==NULL)
		return false;
	item->add_dval(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddDatetime(int key,time_t value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::DATETIME);
	if(item==NULL)
		return false;
	item->add_tval(value);
	value=htonll(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddBool(int key,bool value)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::BOOL);
	if(item==NULL)
		return false;
	item->add_bval(value);
	//ADD_VALUE(item,value);
	return true;
}
bool PublishHelper::AddComplex(int key,int64_t a,const std::string& b)
{
	MsgExpress::DataItem* item=CreateItem(key,MsgExpress::COMPLEX);
	if(item==NULL)
		return false;
	MsgExpress::Complex* com=item->add_compval();
	com->set_a(a);
	com->set_b(b);
	uint64_t value=htonll(a);
	//ADD_VALUE(item,value);
	return true;
}
MsgExpress::DataItem* PublishHelper::GetItem(int key)
{
	if(mapItems.find(key)==mapItems.end())
		return NULL;
	return mapItems[key];
}

PublishHelper::PublishHelper(MsgExpress::PublishData* pub,bool load)
{
	sub=NULL;
	loaded=load;
	mapItems.clear();
	if(loaded)
	{
		for(int i=0;i<pub->item_size();i++)
		{
			MsgExpress::DataItem* item=new MsgExpress::DataItem(pub->item(i));
			mapItems[item->key()]=item;
			//for( int j=0;j<item->value_size();j++)
			//{
			//	if(item->type()==MsgExpress::STRING)
			//		item->add_strval(item->value(j));
			//	else if(item->type()==MsgExpress::INT32)
			//		item->add_ival(Converter::toInt32(item->value(j)));
			//	else if(item->type()==MsgExpress::UINT32)
			//		item->add_uival(Converter::toUInt(item->value(j)));
			//	else if(item->type()==MsgExpress::INT64)
			//		item->add_lval(Converter::toInt64(item->value(j)));
			//	else if(item->type()==MsgExpress::UINT64)
			//		item->add_ulval(Converter::toUInt64(item->value(j)));
			//	else if(item->type()==MsgExpress::FLOAT)
			//		item->add_fval(Converter::toFloat(item->value(j)));
			//	else if(item->type()==MsgExpress::DOUBLE)
			//		item->add_dval(Converter::toDouble(item->value(j)));
			//	else if(item->type()==MsgExpress::DATETIME)
			//		item->add_tval(Converter::toUInt64(item->value(j)));
			//}
		}
	}
	else
		this->pub=pub;
}
PublishHelper::PublishHelper(MsgExpress::SubscribeData* sub)
{
	pub=NULL;
	loaded=false;
	this->sub=sub;
	mapItems.clear();
}
PublishHelper::~PublishHelper()
{
	if(loaded)
	{
		std::unordered_map<int,MsgExpress::DataItem*>::const_iterator it=mapItems.begin();
		while(it!=mapItems.end())
		{
			delete it->second;
			it++;
		}
	}
	mapItems.clear();
}